import requests,json,traceback,jsonpath
from HTTP.commonKeys import sysKey
from common import logger

class HTTP:
    """
    接口自动化架构体系
    数据驱动运行入口
    Powered By Mr.shuhao
    """
    def __init__(self,writer):
        # 创建会话管理
        self.session = requests.session()
        # 设置请求默认的头
        self.session.headers["content-type"] = 'application/x-www-form-urlencoded'
        self.session.headers['user-agent'] ='Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'
        # 项目接口基本地址
        self.url = ''
        # 保存请求后的结果
        self.result = None
        # 保存json解析后的字典
        self.jsonres = None
        # 写入结果的对象
        self.writer = writer
        # 记录关键字运行时，写入excel的行
        self.row = 0

    def seturl(self,url):
        """
        设置项目接口基本地址
        :param url:传入项目基本地址
        :return:成功/失败
        """
        # 对传入参数的处理
        if url is None or url == '':
            url = ''
        self.url = url
        self.__write_excel(True,self.url)
        return True

    def get(self,path,params):
        """
        以data字典形式传键值对参数
        :param path: 拼接url最后的接口地址
        :param params:字典参数
        :return:成功/失败
        """
        # 对传入参数的处理
        if params is None or params == '':
            params = None

        # 对path进行处理
        if path is None or path == "":
            # 接口地址不应该为空
            self.__write_excel(False,"接口路径有误，请检查")
            return False

        # 实现关联
        params = self.__get_relations(params)

        # 如果传入非绝对路径的地址，就拼接上基础地址
        if not path.startswith('https'):
            path = self.url + '/' + path

        # 处理url链接失败的情况
        try:
            self.result = self.session.get(path + '?' + params)
        except Exception as e:
            self.result = None

        try:
            # 如果返回的是json字符串，就处理为字典
            resulttext =self.result.text
            resulttext = resulttext[resulttext.find('{'):resulttext.rfind('}')+1]
            self.jsonres = json.loads(resulttext)
            self.__write_excel(True,self.jsonres)
        except Exception as e:
            logger.exception(e)
            self.jsonres = None
            if self.result is None:
                self.__write_excel(False,None)
            else:
                self.__write_excel(True,self.result.text)

        return True

    def post(self,path,params):
        """
        以data字典形式传键值对参数
        :param path:拼接url最后的地址路径
        :param params:字典参数
        :return:成功/失败
        """
        # 对传入参数的处理
        if params is None or params == '':
            params = None

        # 对path进行处理
        if path is None or path == '':
            print(False, '接口路径输入有误,请检查')
            return False

        # 参数实现关联
        params = self.__get_relations(params)
        params = self.__get_data(params)

        # 如果传非绝对路径的地址,就拼接基础地址
        if not path.startswith('https'):
            path = self.url + "/" + path

        # 处理url链接失败的情况
        try:
            self.result = self.session.post(path,data=params)
        except Exception as e:
            self.result = None

        # 如果返回的是json字符串，就处理为字典
        try:
            resulttext = self.result.text
            resulttext = resulttext[resulttext.find('{'):resulttext.rfind('}'+1)]
            self.jsonres = json.loads(resulttext)
            self.__write_excel(True,self.jsonres)
            print(str(self.jsonres))
        except Exception as e:
            logger.exception(e)
            print(self.result.text)
            print(str(traceback.format_exc()))
            self.jsonres = None
            if self.result is None:
                self.__write_excel(False,None)
            else:
                self.__write_excel(True,self.result.text)
        return True

    def postnodata(self,path,params):
        """
        以data字典形式传键值对参数
        :param path: 接口地址
        :param params: 参数字典
        :return: 成功/失败
        """
        # 对传入参数的处理
        if params is None or params == '':
            params = None

        # 对path进行处理
        if path is None or path == '':
            # 接口地址不应该为空
            self.__write_excel(False, "接口名字错误")
            return False

        # 实现关联
        params = self.__get__relations(params)

        # 如果传非绝对路径的地址，就拼上基础地址
        if not path.startswith("https"):
            path = self.url + "/" + path

        # 处理url连接失败的情况
        try:
            self.result = self.session.post(path, data=params)
        except Exception as e:
            self.result = None

        try:
            # 如果返回的是json字符串，就处理为字典
            resulttext = self.result.text
            resulttext = resulttext[resulttext.find('{'):resulttext.rfind('}') + 1]
            self.jsonres = json.loads(resulttext)
            self.__write_excel(True, self.jsonres)
        except Exception as e:
            logger.exception(e)
            self.jsonres = None
            if self.result is None:
                self.__write_excel(False, None)
            else:
                self.__write_excel(True, self.result.text)

        return True

    def addheader(self,key,value):
        """
        往请求头里添加一个键值对
        :param key: 头的键
        :param value: 头的值
        :return: 成功/失败
        """
        value = self.__get_relations(value)
        self.session.headers[key] = value
        self.__write_excel(True,self.session.headers)
        return True

    def removeheader(self,key):
        """
        从请求头删除一个键值对
        :param key: 需要删除的键
        :return: 成功/失败
        """
        try:
            self.session.headers.pop(key)
        except Exception as e:
            pass

        self.__write_excel(True,self.session.headers)
        return True

    def assertequals(self,key,value):
        """
        断言json结果里面某个键的值和value相等
        :param key: json
        :param value: 期望值
        :return: 是否相等
        """
        # 如果请求返回不是json,就直接reture
        if self.jsonres is None:
            self.__write_excel(False,None)
            print(self.jsonres)
            return True
        value = self.__get_relations(value)
        try:
            # jsonpath用来解析多层嵌套的json数据
            actual = str(jsonpath.jsonpath(self.jsonres,key)[0])
            if actual == str(value):
                self.__write_excel(True,actual)
                return True
            else:
                self.__write_excel(False,actual)
                print("预期结果：" + str(value))
                print("实际结果：" + str(actual))
                return False
        except Exception as e:
            # 处理键不存在的情况
            self.__write_excel(False,traceback.format_exc())
            print(str(traceback.format_exc()))
            return False

    def savejson(self,jsonkey,paramname):
        """
        从jsonres里面保存某个键的值,用来关联
        :param jsonkey: 需要保存的json的键
        :param paramname: 保存后参数的名字
        :return: 成功/失败
        """
        # 去jsonres里面取值
        try:
            value = str(jsonpath.jsonpath(self.jsonres,jsonkey)[0])
        except Exception as e:
            value = None

        # 保存键值对到关联字典里
        sysKey.relations[paramname] = value
        self.__write_excel(True,sysKey.relations)
        return True

    def assertqualjson(self,jsonp):
        """
        断言json结果里多个键值对相等
        :param jsonp: 传入需要比较的多个键值对的json字符串
        :return: 成功/失败
        """
        # 请求返回的结果不是json,就直接reture
        if self.jsonres is None:
            self.__write_excel(False,None)
            print('断言json结果,返回的结果不是json:'+ self.jsonres)
            return False

        try:
            # 传入的参数不是json，传参错误
            jsonp = json.loads(jsonp)
        except Exception as e:
            self.__write_excel(False,traceback.format_exc())
            print('传入的参数不是json，传参错误')
            return False

        try:
            # 处理传入参数的键不存在的情况
            for key in jsonp.key():
                # 只要有一个键值对不相等,就不相等，断言失败
                value = str(jsonpath.jsonpath(self.jsonres,key)[0])
                if not value == str(jsonp[key]):
                    self.__write_excel(False,self.jsonres)
                    return False
            # 所有的键值对都相等,才返回True
            self.__write_excel(True,self.jsonres)
            return True
        except Exception as e:
            self.__write_excel(False,traceback.format_exc())
            return False

    def assertontains(self,value):
        """
        断言返回结果的字符串包含value
        :param value: 被包含的字符串
        :return: 是否包含
        """
        try:
            # 如果返回结果为空,就报错
            result = str(self.result.text)
        except Exception as e:
            self.__write_excel(False,traceback.format_exc())
            return False
        # 关联
        value = self.__get_relations(value)

        if result.__contains__(str(value)):
            self.__write_excel(True,self.result.text)
            return True
        else:
            self.__write_excel(False,self.result.text)
            return False

    def __get_data(self,params):
        """
        url参数转为字典
        :param params:传入需要转字典的参数
        :return:转换后的字典
        """
        if params is None:
            return None

        # 定义一个字典,用来保存转换后的参数
        params = {}
        p1 = params.split('&')
        for keyvalue in p1:
            index = keyvalue.find('=')
            if index >= 0:
                key = keyvalue[0:index]
                value = keyvalue[index + 1:]
                params[key] = value
            else:
                params[keyvalue] = ''
        return params

    def __get_relations(self,params):
        """
        使用关联结果
        :param params:传入需要关联的参数
        :return:返回关联后的字符串
        """
        if params is None:
            return None

        for key in sysKey.relations.keys():
            params = params.replace('{' + key + '}',str(sysKey.relations[key]))
        return params

    def __write_excel(self,status,msg):
        """
        写入关键字运行结果
        :param status: 运行的状态
        :param msg: 实际运行的结果
        :return: 无
        """
        if status is True:
            self.writer.write(self.row,7,"PASS",3)
        else:
            self.writer.write(self.row,7,"FAIL",2)

        # 处理实际结果过长,只保存32767个字符
        msg = str(msg)
        if len(msg) > 32767:
            msg = msg[0:32767]

        self.writer.write(self.row,8,str(msg))

























